package httpx

import (
	"context"
	ctxconstans "gitee.com/zackeus/go-boot/common/constants/context"
	"gitee.com/zackeus/go-boot/common/constants/net/headers"
	"gitee.com/zackeus/goutil/strutil"
	"net"
	"net/http"
	"strings"
)

// IsLocalIP4Addr 检测 IP 地址字符串是否是内网地址
func IsLocalIP4Addr(ip string) bool {
	return IsLocalIP4(net.ParseIP(ip))
}

// IsLocalIP4 检测 IP 地址是否是内网地址
// 通过直接对比ip段范围效率更高，详见：https://github.com/thinkeridea/go-extend/issues/2
func IsLocalIP4(ip net.IP) bool {
	if ip.IsLoopback() {
		return true
	}

	ip4 := ip.To4()
	if ip4 == nil {
		return false
	}

	return ip4[0] == 10 || // 10.0.0.0/8
		(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
		(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
		(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
}

// ClientIP 尽最大努力实现获取客户端 IP 的算法。
// 解析 X-Real-IP 和 X-Forwarded-For 以便于反向代理（nginx 或 haproxy）可以正常工作
// see https://github.com/thinkeridea/go-extend/blob/master/exnet/ip.go
func ClientIP(r *http.Request) (string, error) {
	xForwardedFor := r.Header.Get(headers.XForwardedFor)
	ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0])
	if !strutil.IsBlank(ip) {
		return ip, nil
	}

	ip = strings.TrimSpace(r.Header.Get(headers.XRealIP))
	if !strutil.IsBlank(ip) {
		return ip, nil
	}

	ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))
	if err != nil {
		return "", err
	}
	return ip, nil
}

// ClientIPFromContext 从context解析客户端IP
func ClientIPFromContext(ctx context.Context) string {
	if ctx == nil {
		return ""
	}
	ipVal := ctx.Value(ctxconstans.ClintIP)
	if ip, ok := ipVal.(string); ok {
		return ip
	}
	return ""
}
